﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using RIABlog.Web.Models;

namespace RIABlog.Web.Helpers
{
	internal static class EntityExtensions
	{
		#region Public Properties

		/// <summary>
		/// Разделитель в имени папки блога или поста
		/// </summary>
		public const char NameSeparator = '.';

		/// <summary>
		/// Разделитель ключевых слов в имени файла поста
		/// </summary>
		public const char KeywordSeparator = ';';

		/// <summary>
		/// Расширение файла с текстом поста
		/// </summary>
		public const string PostFileExtension = ".post";

		/// <summary>
		/// Расширение файла с текстом комментария
		/// </summary>
		public const string CommentFileExtension = ".comment";

		/// <summary>
		/// Путь к папке со всеми блогами
		/// </summary>
		public static string BaseFolder
		{
			get
			{
				string baseFolder = HttpContext.Current.Request.PhysicalApplicationPath + @"Blogs";
				Directory.CreateDirectory(baseFolder);
				return baseFolder;
			}
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Ищет папку блога по идентификатору в имени папки
		/// </summary>
		/// <returns></returns>
		public static string GetExistingBlogFolder(Guid blogId)
		{
			// для простоты забиваем на специфическую обработку исключений
			return
				(
					from 
						authorFolder in Directory.GetDirectories(BaseFolder)
					from 
						blogFolder in Directory.GetDirectories(authorFolder, "*" + NameSeparator + blogId)
					select 
						blogFolder
				)
				.Single();
		}

        /// <summary>
        /// Ищет папку поста по идентификатору в имени папки
        /// </summary>
        /// <returns></returns>
        public static string GetExistingBlogPostFolder(Guid postId)
        {
            // для простоты забиваем на специфическую обработку исключений
            return
                (
                    from 
						authorFolder in Directory.GetDirectories(BaseFolder)
                    from 
						blogFolder in Directory.GetDirectories(authorFolder)
                    from 
						postFolder in Directory.GetDirectories(blogFolder, "*" + NameSeparator + postId)
                    select 
						postFolder
                )
                .Single();
        }

		/// <summary>
		/// Ищет папку блога по идентификатору в имени папки
		/// </summary>
		/// <returns></returns>
		public static string GetExistingFolder(this Blog blog)
		{
			return GetExistingBlogFolder(blog.Id);
		}

		/// <summary>
		/// Возвращает имя папки для блога
		/// </summary>
		/// <param name="blog"></param>
		/// <returns></returns>
		public static string GetNewFolder(this Blog blog)
		{
			return BaseFolder
				+ Path.DirectorySeparatorChar
				+ blog.Author.ToValidPath()
				+ Path.DirectorySeparatorChar
				+ blog.Title
				+ NameSeparator
				+ blog.Id;
		}

        /// <summary>
        /// Ищет папку поста по идентификатору в имени папки
        /// </summary>
        /// <param name="post"></param>
        /// <returns></returns>
        public static string GetExistingFolder(this BlogPost post)
        {
            return GetExistingBlogPostFolder(post.Id);
        }

		/// <summary>
		/// Возвращает имя папки для поста
		/// </summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public static string GetNewFolder(this BlogPost post)
		{
			return GetExistingBlogFolder(post.BlogId)
				+ Path.DirectorySeparatorChar
				+ post.Title
				+ NameSeparator
				+ post.Id;
		}

		/// <summary>
		/// Возвращает имя файла для текста поста
		/// </summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public static string GetFileName(this BlogPost post)
		{
			string keywords = string.Empty;

			if(post.Keywords != null)
			{
				keywords = post.Keywords.Aggregate
					(
						KeywordSeparator.ToString(),
						(current, k) => current + (k.Title + KeywordSeparator)
					);
			}

			return post.GetNewFolder()
				+ Path.DirectorySeparatorChar
				+ keywords
				+ PostFileExtension;
		}

		/// <summary>
		/// Возвращает имя файла для комментария
		/// </summary>
		/// <param name="comment"></param>
		/// <returns></returns>
		public static string GetFileName(this Comment comment)
		{
			return GetExistingBlogPostFolder(comment.PostId)
				+ Path.DirectorySeparatorChar
                + comment.Author.ToValidPath()
                + NameSeparator
                + comment.Id
                + CommentFileExtension;
		}

		/// <summary>
		/// Возвращает автора блога
		/// </summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public static string GetBlogAuthor(this BlogPost post)
		{
			var blogFolder = new DirectoryInfo(GetExistingBlogFolder(post.BlogId));

			return blogFolder.Parent == null ? string.Empty : blogFolder.Parent.Name.FromValidPath();
		}

        /// <summary>
        /// Перебирает все файлы с постами и из их имен извлекает ключевые слова
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetAllKeywords()
        {
            return 
            (
                from 
					authorFolder in Directory.GetDirectories(BaseFolder)
                from 
					blogFolder in Directory.GetDirectories(authorFolder)
                from 
					postFolder in Directory.GetDirectories(blogFolder)
                from 
					fileFullName in Directory.GetFiles(postFolder, "*" + PostFileExtension)
                let 
					fileName = Path.GetFileNameWithoutExtension(fileFullName)
                from 
					keywordWithSpaces in fileName.Split(KeywordSeparator)
				let 
					keyword = keywordWithSpaces.Trim()
				where 
					!string.IsNullOrEmpty(keyword)
                select 
					keyword
            );
        }

		/// <summary>
		/// Извлекает из имени файла список ключевых слов и возвращает их с указанием популярности
		/// </summary>
		/// <param name="fileName"></param>
		/// <returns></returns>
        public static IEnumerable<Keyword> ExtractKeywordsFromFileName(string fileName)
        {
			// все ключевые слова из всех постов
        	var allKeywords = GetAllKeywords();
			// ключевые слова из данного имени файла
        	var postKeywords = Path.GetFileNameWithoutExtension(fileName).Split
                (
                    new [] {KeywordSeparator}, 
                    StringSplitOptions.RemoveEmptyEntries
                );
 
			return
				from 
					postKeyword in postKeywords
				join 
					keyword in allKeywords on postKeyword.ToUpper() equals keyword.ToUpper() 
				group 
					postKeyword by postKeyword into keywordGroup
        		select new Keyword
        			{
						Title = keywordGroup.Key,
						Popularity = keywordGroup.Count()
        			};
        }

		/// <summary>
		/// Заменяет обратный слэш на кавычку. Простое решение проблемы с логином под доменной учеткой (domain\username)
		/// </summary>
		/// <param name="str"></param>
		/// <returns></returns>
		public static string ToValidPath(this string str)
		{
			return str.Replace(@"\", @"`");
		}

		/// <summary>
		/// Преобразует кавычку в обратный слэш. Простое решение проблемы с логином под доменной учеткой (domain\username)
		/// </summary>
		/// <param name="str"></param>
		/// <returns></returns>
		public static string FromValidPath(this string str)
		{
			return str.Replace(@"`", @"\" );
		}

	    #endregion
	}
}